/*
 * main.cpp
 *
 *  Created on: Feb 4, 2010
 *      Author: Jamie LaPointe
 */

#include <pthread.h>
#include <semaphore.h>
#include <iostream>

using std::cout;
using std::cerr;
using std::endl;

#define NUM_THREADS 5
#define TCOUNT 10
#define COUNT_LIMIT 12

int count = 0;
int thread_ids[3] =
{ 0, 1, 2 };

pthread_mutex_t count_mutex;
pthread_cond_t count_threshold_cv;
sem_t count_sem;

void * inc_count(void * t)
{
   int i;
   long my_id = (long) t;

   for (i = 0; i < TCOUNT; ++i)
   {
      pthread_mutex_lock(&count_mutex);
      ++count;

      //
      // check the value of count and signal waiting thread when condition is
      // reached.  Note that this occurs while mutex is locked.
      //
      if (count == COUNT_LIMIT)
      {
         pthread_cond_signal(&count_threshold_cv);
         cout << "inc_count(): thread " << my_id << ", count = " << count
               << "  Threshold reached." << endl;
      }

      cout << "inc_count(): thread " << my_id << ", count = " << count
            << ", unlocking mutex" << endl;
      pthread_mutex_unlock(&count_mutex);

      //
      // pretend to do some work so threads can alternate on mutex lock
      //
      sleep(1);
   }

   //
   // signal to the main thread that this thread has finished processing its data
   //
   sem_post(&count_sem);

   pthread_exit(0);
   return 0;
}

void * watch_count(void * t)
{
   long my_id = (long)t;

   cout << "Starting watch_count(): thread " << my_id << endl;

   //
   // Lock mutex and wait for signal.  Note that the pthread_cond_wait
   // routine will automatically and atomically unlock mutex while it waits.
   // Also, note that if COUNT_LIMIT is reached before this routine is run by
   // the waiting thread, the loop will be skipped to prevent pthread_cond_wait
   // from never returning.
   //
   pthread_mutex_lock(&count_mutex);
   if (count < COUNT_LIMIT)
   {
      pthread_cond_wait(&count_threshold_cv, &count_mutex);
      cout << "watch_count(): thread " << my_id << " Condition signal recieved." << endl;
      count += 125;
      cout << "watch_count(): thread " << my_id << " count now = " << count << endl;
   }
   pthread_mutex_unlock(&count_mutex);

   //
   // Signal to the main thread that this thread has finished its work.
   //
   sem_post(&count_sem);

   pthread_exit(0);
   return 0;
}

int main()
{
   int i;
   long t[NUM_THREADS];

   pthread_t threads[NUM_THREADS];
   pthread_attr_t attr;

   for (i = 0; i < NUM_THREADS; ++i)
   {
      t[i] = i;
   }

   //
   // Init thread variables
   //
   pthread_mutex_init(&count_mutex, NULL);
   pthread_cond_init(&count_threshold_cv, NULL);
   sem_init(&count_sem, NULL, 0);

   //
   // We will be creating detached threads (and using semaphores to synchronize
   // with the main thread)
   //
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

   //
   // Create the watch threads out of 1/3 of our thread pool.
   //
   for (i = 0; i < (NUM_THREADS / 3); ++i)
   {
      pthread_create(&threads[i], &attr, watch_count, (void *)t[i]);
   }

   //
   // Create the inc threads out of the rest of the thread pool (2/3).
   //
   for (; i < NUM_THREADS; ++i)
   {
      pthread_create(&threads[i], &attr, inc_count, (void *)t[i]);
   }

   //
   // wait for worker threads to complete
   //
   for (i = 0; i < NUM_THREADS; ++i)
   {
      while (sem_wait(&count_sem) != 0) {} // ignore the EINTR errors
   }

   cout << "Main(): Waited on " << NUM_THREADS << " threads. Done." << endl;

   //
   // cleanup after yourself
   //
   pthread_attr_destroy(&attr);
   pthread_mutex_destroy(&count_mutex);
   pthread_cond_destroy(&count_threshold_cv);
   sem_destroy(&count_sem);
   pthread_exit(0);

   return 0;
}
